using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.IO;
using System.Text;
using System.Windows.Forms;

namespace Developex.GenerateClassOnShema.Classes
{
	public static class Generator
	{
		#region Templates

		/// <summary>
		/// 0 - Mapped Fields region
		/// 1 - Constructors region
		/// 2 - MaxLength region
		/// 3 - Fields region
		/// 4 - Properties region
		/// 5 - Class name
		/// 6 - Namespace
		/// 7 - 'using' block
		/// </summary>
		private const string CLASS_TEPMLATE =
@"{7}
namespace {6}
{{
%/// <summary>
%/// 
%/// </summary>
%public enum {5}_Fields
%{{
%%#region Mapped Fields
{0}
%%#endregion
%}}

%/// <summary>
%/// 
%/// </summary>
%public class {5} : Row
%{{
%%#region Custom Init

%%private void CustomInit()
%%{{
%%%// make initialization like custom fields adding
%%}}

%%protected virtual IField CreateCustomField({5}_Fields propertyName)
%%{{
%%%// create custom field by field name
%%%return null;
%%}}

%%private IField CreateCustomField(string propertyName)
%%{{
%%%// create custom field by field name
%%%return CreateCustomField(({5}_Fields)Enum.Parse(typeof({5}_Fields), propertyName, true));
%%}}

%%#endregion

%%#region Constructors
{1}
%%#endregion

%%#region MaxLength
{2}
%%#endregion

%%#region Fields
{3}
%%#endregion

%%#region Properties
{4}
%%#endregion
%}}
}}
";

		private const string USING_NORMAL = @"using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Reflection;
using Developex.DatabaseWrapper;
using Developex.DatabaseWrapper.Fields;
using Developex.DatabaseWrapper.Attributes;
";

		private const string USING_CE = @"using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using Developex.DatabaseWrapperCE;
using Developex.DatabaseWrapperCE.Fields;
using Developex.DatabaseWrapperCE.Attributes;
";

		/// <summary>
		/// 0 - className
		/// 1 - table_name
		/// 2 - AddGeneratedFields content
		/// 3 - switch content in CreateAndAddField(string propertyName)
		/// </summary>
		private const string CONSTRUCTOR_TEMPLATE = @"
%%static public readonly {0} SampleItem = new {0}();

%%protected override Row GetSampleItem()
%%{{
%%%return SampleItem;
%%}}

%%/// <summary>
%%/// Default Constructor
%%/// </summary>
%%public {0}()
%%%: this(true, true)
%%{{
%%}}

%%/// <summary>
%%/// Constructor
%%/// </summary>
%%/// <param name=""createFields""></param>
%%/// <param name=""customInit""></param>
%%public {0}(bool createFields, bool customInit)
%%%: base(""{1}"")
%%{{
%%%if (createFields)
%%%%AddGeneratedFields();
%%%if (customInit)
%%%%CustomInit();
%%}}

%%/// <summary>
%%/// Constructor, with custom table name
%%/// </summary>
%%/// <param name=""tableName"">Table name</param>
%%public {0}(string tableName)
%%%: base(tableName)
%%{{
%%%AddGeneratedFields();
%%%CustomInit();
%%}}

%%/// <summary>
%%/// Create only given field
%%/// </summary>
%%/// <param name=""fieldsToCreate"">Fields to create</param>
%%public {0}(params Enum[] fieldsToCreate)
%%%: this(""{1}"", fieldsToCreate)
%%{{
%%}}

%%/// <summary>
%%/// Create only given field, with custom table name
%%/// </summary>
%%/// <param name=""tableName"">Table name</param>
%%/// <param name=""fieldsToCreate"">Fields to create</param>
%%public {0}(string tableName, params Enum[] fieldsToCreate)
%%%: base(tableName)
%%{{
%%%if (fieldsToCreate.Length == 0)
%%%{{
%%%%AddGeneratedFields();
%%%%CustomInit();
%%%%return;
%%%}}
%%%foreach (Enum fieldToCreate in fieldsToCreate)
%%%{{
%%%%CreateAndAddField(fieldToCreate);
%%%}}
%%}}

%%#region Fields Creation

%%/// <summary>
%%/// Create object for clone
%%/// </summary>
%%/// <returns/>
%%/// <remarks>
%%/// NB!: don't add any fields here
%%///              Here should be only object creation, all data copy should be in overrided Clone function.
%%///              Used only in base version of Clone function.
%%///              Should be overrided for ICloneable interface.
%%/// </remarks>
%%protected override Row CreateObjectForClone()
%%{{
%%%return new {0}(false, false);
%%}}

%%protected internal void AddGeneratedFields()
%%{{{2}
%%}}

%%/// <summary>
%%/// Add field by it's enum
%%/// </summary>
%%/// <param name=""propertyName""></param>
%%public override void CreateAndAddField(Enum propertyName)
%%{{
%%%CreateAndAddField(propertyName.ToString());
%%}}

%%/// <summary>
%%/// Create and add field by mapped property name
%%/// </summary>
%%/// <param name=""propertyName""></param>
%%public override void CreateAndAddField(string propertyName)
%%{{
%%%IField fieldToAdd = null;
%%%switch (propertyName)
%%%{{{3}
%%%}}
%%%if (fieldToAdd == null)
%%%%fieldToAdd = CreateCustomField(propertyName);
%%%if (fieldToAdd == null)
%%%%throw new InvalidOperationException(""Cannot find field with mapped property = "" + propertyName);
%%%AddField(fieldToAdd);
%%}}

%%#endregion
";
		/// <summary>
		/// 0 - field type, basically Field
		/// 1 - table field name
		/// 2 - other parameters
		/// </summary>
		private const string CreateField_TEMPLATE = "new {0}(\"{1}\"{2})";

		/// <summary>
		/// 0 - property name
		/// 1 - field name
		/// 2 - CreateField string
		/// </summary>
		private const string CreateAndAddField_switch_TEMPLATE = @"
%%%%case ""{0}"":
%%%%%fieldToAdd = {1} = {2};
%%%%%break;";

		/// <summary>
		/// 0 - field name
		/// 1 - CreateField string
		/// </summary>
		private const string CreateAndAddField_TEMPLATE = @"
%%%{0} = {1};
%%%AddField({0});";

		/// <summary>
		/// 0 - Property name
		/// </summary>
		private const string MAXLENGTH_TEMPLATE = @"
%%/// <summary>
%%/// Max Length for property {0}
%%/// </summary>
%%public static readonly int {0}MaxLength = {1};
";

		/// <summary>
		/// Field declaration
		/// 0 - property name
		/// 1 - field type, basically Field
		/// 2 - field name
		/// </summary>
		private const string FIELD_TEMPLATE = @"
%%/// <summary>
%%/// </summary>
%%[PropertyName(""{0}"")]
%%protected internal {1} {2} = null;
";

		/// <summary>
		/// Property declaration
		/// 0 - comment
		/// 1 - field underlying type<type>
		/// 2 - property name
		/// 3 - field name
		/// </summary>
		private const string PROPERTY_TEMPLATE = @"
%%/// <summary>
%%/// {0}
%%/// </summary>
%%public {1} {2}
%%{{
%%%get {{ return {3}; }}
%%%set {{ {3}.Value = value; }}
%%}}
";

		/// <summary>
		/// Field Property declaration
		/// 1 - field type (basically Field)<type>
		/// 2 - property name
		/// 3 - field name
		/// </summary>
		private const string FIELD_PROPERTY_TEMPLATE = @"
%%/// <summary>
%%/// </summary>
%%public {0} _{1}
%%{{
%%%get {{ return {2}; }}
%%}}
";

		/// <summary>
		/// Property declaration
		/// 0 - comment
		/// 1 - field type basically Field<type>
		/// 2 - property name
		/// 3 - field name
		/// 4 - enumeration type
		/// </summary>
		private const string PROPERTY_TEMPLATE_ENUM = @"
%%/// <summary>
%%/// {0}
%%/// </summary>
%%public {4} {2}
%%{{
%%%get {{ return ({4}){3}.Value; }}
%%%set {{ {3}.Value = ({1})value; }}
%%}}
";

		/// <summary>
		/// Property declaration - encoded value
		/// 0 - comment
		/// 1 - property name
		/// 2 - field name
		/// </summary>
		private const string ENCODED_PROPERTY_TEMPLATE = @"
%%/// <summary>
%%/// {0} (html encoded)
%%/// </summary>
%%public string {1}_Encoded
%%{{
%%%get {{ return HttpUtility.HtmlEncode({2}); }}
%%}}
";

		/// <summary>
		/// Property declaration - foregin object
		/// 0 - comment
		/// 1 - foreign object type
		/// 2 - property name
		/// 3 - field name
		/// </summary>
		private const string FK_PROPERTY_TEMPLATE = @"
%%/// <summary>
%%/// {0}
%%/// </summary>
%%public {1} {2}
%%{{
%%%get {{ return {3}.Object; }}
%%}}
";
		/// <summary>
		/// HtmlEncode for multiline text
		/// 0 - comment
		/// 1 - property name
		/// 2 - field name
		/// </summary>
		private const string ENCODED_WITHBREAKES_PROPERTY_TEMPLATE = @"
%%/// <summary>
%%/// {0} (html encoded with line breakes)
%%/// </summary>
%%public string {1}_Encoded
%%{{
%%%get {{ return HelperFunctions.HtmlEncodeWithLineBreaks({2}); }}
%%}}
";

		#endregion

		#region Public functions

		public static void GenerateClassesForDatabase(string iniFileName, string outputFolder, string connectionString, string nameSpace, string indentSize, bool CE, bool addEncoded, bool isSql2000)
		{
			try
			{
				if (connectionString.IndexOf(';') == -1)
					connectionString = string.Format(@"Server=.\SQLExpress;Database={0};Integrated Security=True;", connectionString);
				DbSettings settings = new DbSettings(nameSpace, indentSize, CE, addEncoded, isSql2000);
				settings.ReadFromIniFile(iniFileName);
				Console.WriteLine("Ini file parsed successfully");
				GenerateClassesForDatabase_impl(settings, outputFolder, connectionString);
				MessageBox.Show("Generated!");
			}
			catch (Exception ex)
			{
				string error = "Error occurred: " + ex.Message;
				MessageBox.Show(error);
			}
		}

		#endregion

		#region Utils

		private static string PropertyNameFromDbFormat(string name)
		{
			string className = "";
			string[] arr = name.Split('_');
			foreach (string s in arr)
			{
				className += getBigWord(s);
			}
			return className;
		}

		#endregion

		#region Implementation

		private static void GenerateClassesForDatabase_impl(DbSettings settings, string outputFolder, string connectionString)
		{
			using (SqlConnection conn = new SqlConnection(connectionString))
			{
				conn.Open();
				IEnumerable<TableSettings> allTableSettings = settings.GetAllTableSettings();
				foreach (TableSettings tableSettings in allTableSettings)
				{
					string tableName = tableSettings.TableName;
					string className = tableSettings.ClassName;
					Console.Write("Processing table " + tableName + "..");
					string fileName = FileUtils.CombinePath(outputFolder, className) + ".cs";
					Table tableObject = DatabaseInfo.ReadTableData(conn, tableName, settings);
					CheckTableProperties(tableObject, tableSettings);

					string regionConstructors;
					string regionMaxLength;
					string regionFields;
					string regionProperties;
					string regionMappedFields;

					GenerateClass_internal(tableObject, settings,
						out regionConstructors,
						out regionMaxLength,
						out regionFields,
						out regionProperties,
						out regionMappedFields);

					string dataTemplate = File.Exists(fileName)
													 ? GetFileContentTemplate(fileName, "Mapped Fields", "Constructors", "MaxLength", "Fields", "Properties")
													 : CLASS_TEPMLATE.Replace("%", settings.IndentSize);

					File.WriteAllText(fileName, string.Format(dataTemplate,
						regionMappedFields,
						regionConstructors,
						regionMaxLength,
						regionFields,
						regionProperties,
						className,
						settings.NameSpace,
						settings.IsCE ? USING_CE : USING_NORMAL));
					Console.WriteLine("done");
				}
				Console.WriteLine("Generate data layer done successfully!");
				conn.Close();
			}
		}

		private static void CheckTableProperties(Table table, SimpleSettings settings)
		{
			foreach (string sectionName in settings.Sections.Keys)
			{
				bool exist = false;
				foreach (Column column in table.Columns)
				{
					if (string.Equals(column.Name, sectionName, StringComparison.CurrentCultureIgnoreCase))
					{
						if (settings.Sections[sectionName].ContainsKey("fk"))
						{
							column.IsForeignKey = true;
							column.ForeignTable = settings.Sections[sectionName]["fk"];
						}
						if (settings.Sections[sectionName].ContainsKey("isnullable"))
						{
							bool isNullable;
							if (!bool.TryParse(settings.Sections[sectionName]["isnullable"], out isNullable))
								throw new InvalidOperationException("IniFile: incorrect value of 'isnullable' setting('" + sectionName + "' in table '" + table.TableName + "')");

							column.IsNullable = isNullable;
						}
						exist = true;
						break;
					}
				}
				if (!exist)
					throw new InvalidOperationException("IniFile: cannot find column '" + sectionName + "' in table '" + table.TableName + "'");
			}
		}

		private static void GenerateClass_internal(Table table, DbSettings settings,
			out string regionConstructors,
			out string regionMaxLength,
			out string regionFields,
			out string regionProperties,
			out string regionMappedFields)
		{
			string className = settings.GetTableClassName(table.TableName);
			TableSettings tableSettings = settings.GetTableSettings(table.TableName);
			string tableName = tableSettings.GetTableSetting(TableSettingsName.View, table.TableName);

			StringBuilder sbRegionFields = new StringBuilder();
			StringBuilder sbRegionProperties = new StringBuilder();
			StringBuilder sbAddGeneratedFields = new StringBuilder();
			StringBuilder sbCreateAndAddFieldByProperty = new StringBuilder();
			StringBuilder sbRegionMaxLength = new StringBuilder();
			StringBuilder sbRegionMappedFields = new StringBuilder();

			sbRegionMappedFields.AppendLine();

			foreach (Column column in table.Columns)
			{
				string fieldsType = GetFieldType(column, settings);
				string columnName = column.Name;
				string propertyName = tableSettings.GetColumnSetting(
					columnName, ColumnSettingsName.PropertyName, PropertyNameFromDbFormat(columnName));
				string fieldName = tableSettings.GetColumnSetting(
					columnName, ColumnSettingsName.FieldName, FieldNameFromPropertyName(propertyName));

				// Format property name
				if (column.IsForeignKey)
					propertyName = AddIdToName(propertyName);

				// precede situation when field name is 'value'
				if (propertyName == fieldName)
					fieldName = "_" + fieldName;
				string getterFieldName = fieldName == "value" ? "this.value" : fieldName;

				// Append Mapped Fields
				sbRegionMappedFields.AppendLine(string.Format("%%{0},", propertyName));

				// base type for field
				string basetype = column.Type;
				if (column.IsNullable)
					basetype = GetNullableType(basetype);

				// APPEND PROPERTY
				string enumName = tableSettings.GetColumnSetting(columnName, ColumnSettingsName.Enum);
				if (!string.IsNullOrEmpty(enumName))
				{
					sbRegionProperties.Append(string.Format(PROPERTY_TEMPLATE_ENUM, "", basetype, propertyName, getterFieldName, column.IsNullable ? enumName + "?" : enumName));
					sbRegionProperties.Append(string.Format(PROPERTY_TEMPLATE, "", basetype, propertyName + "_Raw", getterFieldName));
				}
				else
					sbRegionProperties.Append(string.Format(PROPERTY_TEMPLATE, "", basetype, propertyName, getterFieldName));
				sbRegionProperties.Append(string.Format(FIELD_PROPERTY_TEMPLATE, fieldsType, propertyName, getterFieldName));

				// for parameters
				List<FieldFlags> flags = new List<FieldFlags>();
				string customParameters = "";

				if (fieldsType == "StringField")
				{
					// DisableNull flag
					if (!column.IsNullable)
						flags.Add(FieldFlags.DisableNull);

					// MaxLength
					if (column.MaxLength != null && column.MaxLength > 0 && column.MaxLength < 10000)
					{
						sbRegionMaxLength.Append(string.Format(MAXLENGTH_TEMPLATE, propertyName, column.MaxLength.Value));
						customParameters = string.Format(", {0}MaxLength", propertyName);
					}

					if (!settings.IsCE && settings.AddEncodedProperty)
					{
						if (column.MaxLength == -1 || column.MaxLength > 100000)
							sbRegionProperties.Append(string.Format(ENCODED_WITHBREAKES_PROPERTY_TEMPLATE, "", propertyName, getterFieldName));
						else
							sbRegionProperties.Append(string.Format(ENCODED_PROPERTY_TEMPLATE, "", propertyName, getterFieldName));
					}
				}

				// Check for several field flags
				if (fieldsType != "IntPrimaryKey")
				{
					if (column.IsPrimaryKey)
						flags.Add(FieldFlags.PrimaryKey);
					if (column.IsIdentity)
						flags.Add(FieldFlags.Identity);
				}

				if (column.IsComputed)
					flags.Add(FieldFlags.Fictitious);

				// APPEND FIELD
				sbRegionFields.Append(string.Format(FIELD_TEMPLATE, propertyName, fieldsType, fieldName));

				if (column.IsForeignKey)
				{
					if (tableSettings.ColumnSettingExists(columnName, ColumnSettingsName.FKUseTableName) ||
						tableSettings.ColumnSettingExists(columnName, ColumnSettingsName.FKColumns))
					{
						string pp = "";
						if (tableSettings.ColumnSettingExists(columnName, ColumnSettingsName.FKUseTableName))
						{
							pp = "\"" + column.ForeignTable + "\"";
						}
						if (tableSettings.ColumnSettingExists(columnName, ColumnSettingsName.FKColumns))
						{
							if (pp.Length > 0)
								pp += ", ";
							pp += "new Enum[]{";
							string fkClassName = settings.GetTableClassName(column.ForeignTable);
							string[] names = tableSettings.GetColumnSetting(columnName, ColumnSettingsName.FKColumns).Split(',');
							for (int i = 0; i < names.Length; i++)
							{
								if (i > 0)
									pp += ", ";
								pp += fkClassName + "_Fields." + names[i].Trim();
							}
							pp += "}";
						}
						customParameters += ", new object[]{" + pp + "}";
					}
				}

				// Create and Add Field
				if (tableSettings.ColumnSettingExists(columnName, ColumnSettingsName.Secured))
					flags.Add(FieldFlags.Secure);
				if (tableSettings.ColumnSettingExists(columnName, ColumnSettingsName.SecuredNoExtraBytes))
					flags.Add(FieldFlags.SecuredNoExtraBytes);
				if (tableSettings.ColumnSettingExists(columnName, ColumnSettingsName.CustomEncrypt))
					flags.Add(FieldFlags.CustomEncrypt);
				if (tableSettings.ColumnSettingExists(columnName, ColumnSettingsName.CustomDecrypt))
					flags.Add(FieldFlags.CustomDecrypt);
				if (tableSettings.ColumnSettingExists(columnName, ColumnSettingsName.HeavyField))
					flags.Add(FieldFlags.HeavyField);
				string parameters = FormatNewParameters(flags, column.DefValue, customParameters);
				string creationString = string.Format(CreateField_TEMPLATE, fieldsType, columnName, parameters);
				sbAddGeneratedFields.Append(string.Format(CreateAndAddField_TEMPLATE, fieldName, creationString));
				sbCreateAndAddFieldByProperty.Append(string.Format(CreateAndAddField_switch_TEMPLATE, propertyName, fieldName, creationString));

				// add Foreign Object property
				if (column.IsForeignKey)
				{
					string fk_property_Name = tableSettings.GetColumnSetting(columnName, ColumnSettingsName.FKPropertyName, RemoveIdFromName(propertyName));
					sbRegionProperties.Append(string.Format(FK_PROPERTY_TEMPLATE, "(Foreign Object)", settings.GetTableClassName(column.ForeignTable), fk_property_Name, getterFieldName));
				}
			}

			regionConstructors = string.Format(CONSTRUCTOR_TEMPLATE, className, tableName, sbAddGeneratedFields, sbCreateAndAddFieldByProperty).Replace("%", settings.IndentSize);
			regionMaxLength = sbRegionMaxLength.ToString().Replace("%", settings.IndentSize);
			regionFields = sbRegionFields.ToString().Replace("%", settings.IndentSize);
			regionProperties = sbRegionProperties.ToString().Replace("%", settings.IndentSize);
			regionMappedFields = sbRegionMappedFields.ToString().Replace("%", settings.IndentSize);
		}

		private static string RemoveIdFromName(string name)
		{
			if (name.EndsWith("Id"))
				return name.Substring(0, name.Length - 2);
			if (name.EndsWith("Code"))
				return name.Substring(0, name.Length - 4);
			return name;
		}

		private static string AddIdToName(string name)
		{
			if (name.EndsWith("ID"))
				return name.Substring(0, name.Length - 2) + "Id";
			if (name.EndsWith("Id") || name.EndsWith("Code"))
				return name;
			return name + "Id";
		}

		private static string FormatNewParameters(ICollection<FieldFlags> flags, object defValue, string customParameters)
		{
			StringBuilder sb = new StringBuilder();

			//if (defValue == null)
			//{
			//   if (!string.IsNullOrEmpty(customParameters))
			//      defValue = "null";
			//}
			//else 
			if (defValue is bool)
				defValue = (bool)defValue ? "true" : "false";
			else if (defValue is string)
				defValue = "@\"" + ((string)defValue).Replace("\"", "\"\"") + "\"";

			if ((!string.IsNullOrEmpty(customParameters) || defValue != null) && flags.Count == 0)
				flags.Add(FieldFlags.None);

			if (flags.Count > 0)
			{
				sb.Append(", ");
				bool first = true;
				foreach (FieldFlags flag in flags)
				{
					if (!first)
						sb.Append(" | ");
					sb.AppendFormat("FieldFlags.{0}", flag);
					first = false;
				}
			}

			if (defValue != null)
				sb.AppendFormat(", {0}", defValue);

			sb.Append(customParameters);

			return sb.ToString();
		}

		private static string GetFieldType(Column column, DbSettings settings)
		{
			string basetype = column.Type;
			string foreignTableClassName = "";
			if (column.IsForeignKey)
				foreignTableClassName = settings.GetTableClassName(column.ForeignTable);
			if (column.IsNullable)
				basetype = GetNullableType(basetype);

			if (column.Type == "int" && column.IsPrimaryKey && column.IsIdentity)
				return "IntPrimaryKey";

			string col_name = column.Name.ToLower();

			if (column.Type == "DateTime" && (col_name == "updated_on" || col_name == "updatedon"))
				return column.IsNullable ? "UpdatedOnField_nullable" : "UpdatedOnField";

			if (column.Type == "DateTime" && (col_name == "created_on" || col_name == "createdon"))
				return "CreatedOnField";

			if (col_name == "created_by" || col_name == "createdby")
			{
				return column.IsForeignKey ? "CreatedByField_FK<" + basetype + ", " + foreignTableClassName + ">"
													: "CreatedByField<" + basetype + ">";
			}

			if (col_name == "updated_by" || col_name == "updatedby")
			{
				return column.IsForeignKey ? "UpdatedByField_FK<" + basetype + ", " + foreignTableClassName + ">"
													: "UpdatedByField<" + basetype + ">";
			}

			if (column.Type == "string")
				return column.IsForeignKey ? "StringField_FK<" + foreignTableClassName + ">" : "StringField";

			if (column.IsForeignKey)
				return "ForeignKeyField<" + basetype + ", " + foreignTableClassName + ">";

			return "Field<" + basetype + ">";
		}

		private static string GetNullableType(string typeName)
		{
			if (typeName == "string" || typeName == "byte[]")
				return typeName;
			return typeName + "?";
		}

		/// <summary>
		/// convert db name to property name
		/// </summary>
		/// <param name="name"></param>
		/// <returns></returns>
		private static string getBigWord(string name)
		{
			if (name.Length < 2)
				return name.ToUpper();
			name = char.ToUpper(name[0]) + name.Substring(1);
			StringBuilder sb = new StringBuilder(name.Length);
			for (int i = 0; i < name.Length; i++)
			{
				char c = name[i];
				if (char.IsUpper(c) && i > 0 && char.IsUpper(name[i - 1]) && (i == name.Length - 1 || char.IsUpper(name[i + 1])))
					c = char.ToLower(c);
				sb.Append(c);
			}
			return sb.ToString();
		}

		private static string FieldNameFromPropertyName(string propertyName)
		{
			if (!char.IsUpper(propertyName[0]))
				return "_" + propertyName + "_";
			int k = 1;
			while (k < propertyName.Length && char.IsUpper(propertyName[k])
				&& (k == propertyName.Length - 1 || char.IsUpper(propertyName[k + 1])))
			{
				k++;
			}
			return propertyName.Substring(0, k).ToLower() + propertyName.Substring(k);
		}

		private static string GetFileContentTemplate(string fileName, params string[] regions)
		{
			Dictionary<string, int> dictRegions = new Dictionary<string, int>();
			for (int i = 0; i < regions.Length; i++)
			{
				dictRegions.Add(regions[i], i);
			}

			FileInfo fileInfo = new FileInfo(fileName);
			StringBuilder sbOuputData = new StringBuilder((int)fileInfo.Length + 4000);

			using (StreamReader sr = File.OpenText(fileName))
			{
				int depth = 0;
				string currentRegion = null;
				while (sr.Peek() != -1)
				{
					string line = sr.ReadLine();
					string trimmedLine = line.Trim();
					if (currentRegion == null)
					{
						sbOuputData.AppendLine(line.Replace("{", "{{").Replace("}", "}}"));
						if (StartsWith(trimmedLine, "#region"))
						{
							string regionName = trimmedLine.Substring("#region".Length).Trim();
							if (dictRegions.ContainsKey(regionName))
							{
								depth = 0;
								sbOuputData.AppendLine("{" + dictRegions[regionName] + "}");
								currentRegion = regionName;
							}
						}
					}
					else
					{
						if (StartsWith(trimmedLine, "#region"))
						{
							depth++;
						}
						else if (StartsWith(trimmedLine, "#endregion"))
						{
							if (depth == 0)
							{
								sbOuputData.AppendLine(line.Replace("{", "{{").Replace("}", "}}"));
								currentRegion = null;
							}
							else depth--;
						}
					}
				}
			}

			return sbOuputData.ToString();
		}

		private static bool StartsWith(string line, string lexem)
		{
			return line.StartsWith(lexem) && (lexem.Length == line.Length || char.IsWhiteSpace(line[lexem.Length]));
		}

		#endregion
	}
}

#region TODO: implement new version

//      private static readonly string ENCODED_LINKTARGETBLANK_PROPERTY_TEMPLATE = @"
//        /// <summary>
//        /// {0} (html with target='_blank' added in all &lt;a&gt; tags)
//        /// </summary>
//        public string {1}_Encoded
//        {{
//            get {{ return HtmlHelper.SetAttributeInAllTags({2}, ""a"", ""target"", ""_blank""); }}
//        }}
//";
//      /// <summary>
//      /// Property declaration - encoded value
//      /// 0 - comment
//      /// 1 - property name
//      /// 2 - field name
//      /// </summary>
//      private static readonly string ENCODED_TEXTLINK_PROPERTY_TEMPLATE = @"
//%%/// <summary>
//%%/// {0} (html encoded and links maked as html link)
//%%/// </summary>
//%%public string {1}_Encoded
//%%{{
//%%%get {{ return HtmlHelper.CreateHtmlLinkInPlainText({2}); }}
//%%}}
//";


// Add _Encoded property
//if (textLinkProperty != null && textLinkProperty.GetProperty(column_name) != null)
//    sbRegionProperties.AppendFormat(ENCODED_TEXTLINK_PROPERTY_TEMPLATE, "", property_Name, getterFieldName);
//else if (htmlLinkProperty != null && htmlLinkProperty.GetProperty(column_name) != null)
//    sbRegionProperties.AppendFormat(ENCODED_LINKTARGETBLANK_PROPERTY_TEMPLATE, "", property_Name, getterFieldName);
//else 
////if (column.MaxLength == -1 || column.MaxLength > 100000)
////   sbRegionProperties.Append(string.Format(ENCODED_WITHBREAKES_PROPERTY_TEMPLATE, "", propertyName, getterFieldName));
////else
////   sbRegionProperties.Append(string.Format(ENCODED_PROPERTY_TEMPLATE, "", propertyName, getterFieldName));


//private static void ReadClassProperties(ref string currentLine, ref int pos, StreamReader sr, List<ClassProperties> classProperties)
//{
//    ClassProperties subClassProperties = null;
//    bool awaitingComma = false;

//    while (true)
//    {
//        while (pos < currentLine.Length && char.IsSeparator(currentLine[pos]))
//            pos++;

//        if (pos == currentLine.Length)
//        {
//            currentLine = sr.ReadLine();
//            pos = 0;

//            if (currentLine == null)
//                throw new InvalidOperationException("Ini file has incorrect format: bad properties format, line data=" + currentLine);

//            continue;
//        }

//        if (currentLine[pos] == ']')
//        {
//            pos++;
//            return;
//        }

//        if ((subClassProperties != null || awaitingComma) && currentLine[pos] == ',')
//        {
//            pos++;
//            subClassProperties = null;
//            awaitingComma = false;
//            continue;
//        }

//        if (subClassProperties != null)
//        {
//            if (currentLine[pos] == '[')
//            {
//                subClassProperties.subProperties = new List<ClassProperties>();
//                pos++;
//                ReadClassProperties(ref currentLine, ref pos, sr, subClassProperties.subProperties);

//                subClassProperties = null;
//                awaitingComma = true;
//                continue;
//            }

//            throw new InvalidOperationException("Ini file has incorrect format: bad symbol after property name, line data=" + currentLine);
//        }

//        if (!char.IsLetterOrDigit(currentLine[pos]) && currentLine[pos] != '_')
//            throw new InvalidOperationException(
//                "Ini file has incorrect format: property name must start from letter and consist of letters and decimal digits, line data=" + currentLine);

//        int propPos = pos;

//        while (pos < currentLine.Length && (char.IsLetterOrDigit(currentLine[pos]) || currentLine[pos] == '_'))
//            pos++;

//        subClassProperties = new ClassProperties();
//        subClassProperties.propertyName = currentLine.Substring(propPos, pos - propPos);

//        classProperties.Add(subClassProperties);
//    }
//}

#endregion